package com.rmkj.trade.match.core;

import com.rmkj.trade.match.core.ob.ChangeData;
import com.rmkj.trade.match.data.TickerData;
import com.rmkj.trade.match.util.IDUtils;
import com.rmkj.trade.match.util.MyLog;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 2019/1/8.
 * 一个商品一个实例
 */
public final class DefaultMatcher extends Matcher {

	private final static MyLog _log = MyLog.getLog(DefaultMatcher.class);

	private final static Map<String, DefaultMatcher> matchers = new HashMap<>();

	private final static Object lock = new Object();

	private static DefaultMatcher create(Goods goods, MatchRule matchRule, OrderStore orderStore){
		return new DefaultMatcher(goods, matchRule, orderStore);
	}

	public static DefaultMatcher getInstance(Goods goods, AbstractDefaultOrderStore orderStore){
		return getInstance(goods, new DefaultMatchRule(), orderStore);
	}

	public static DefaultMatcher getInstance(Goods goods, MatchRule matchRule){
		return getInstance(goods, matchRule, new DefaultOrderStore());
	}

	public static DefaultMatcher getInstance(Goods goods, MatchRule matchRule, OrderStore orderStore){
		if(null == goods || null == goods.getSymbol() || "".equals(goods.getSymbol().trim())){
			_log.error("this goods[{}] is not valid", goods);
			return null;
		}
		if(matchRule == null){
			_log.error("matchRule cannot be null");
			return null;
		}
		synchronized (lock){
			if(!matchers.containsKey(goods.getSymbol())){
				DefaultMatcher matcher = create(goods, matchRule, orderStore);
				matcher.loadData(null);
				matcher.loadUnfinishedOrder(null);
				matchers.put(goods.getSymbol(), matcher);
			}
			return matchers.get(goods.getSymbol());
		}
	}

	// 是否debug模式，debug模式时打印日志
	boolean debug = false;

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}

	// 0 未启动 1 在运行 3 在关闭
	int flag = 0;
	// 叫停标识
	boolean stop = false;
	// 交易商品信息
	Goods goods = null;
	// 撮合规则
	MatchRule matchRule = null;
	// 存储订单
	OrderStore orderStore = null;
	// 撮合线程
	Thread worker = null;
	// 撮合状态，是否正在撮合
	volatile boolean matching = false;

	// 最新成交价
	volatile String lastPrice = null;
	// 今开 昨收 最低 最高
	volatile String ticker_open = null, ticker_close = null, ticker_low = null, ticker_high = null;

	private DefaultMatcher(Goods goods, MatchRule matchRule, OrderStore orderStore){
		this.goods = goods;
		this.matchRule = matchRule;
		this.orderStore = orderStore;

		addDefaultObs();
	}

	public OrderStore getOrderStore() {
		return orderStore;
	}

	private void loop(){
		_log.info("matcher[{}] is started successfully", goods);

		List<Order> needRemove = new ArrayList<>();
		MatchRule rule = this.matchRule;
		OrderStore store = this.orderStore;

		f:
		while (!stop){
			needRemove.forEach(it -> {
				store.remove(it);
				if (debug)	_log.debug("finish -> {}", it);
			});
			needRemove.clear();
			store.putReal();
			matching = false;
			// 结束撮合交易，进入等待

			try {
				if (!isOpen()) {
					TimeUnit.MILLISECONDS.sleep(1000);
					continue f;
				}
				TimeUnit.MILLISECONDS.sleep(100);
			} catch (InterruptedException e) {
				flag = 0;
				_log.error(e, "matcher[{}] error, need to restart", goods);
				return;
			}

			// 开始撮合交易
			matching = true;

			Iterator<Order> sellOrders = this.orderStore.sellOrders(), buyOrders = this.orderStore.buyOrders();
			Order sell, buy;

			a:
			while (sellOrders.hasNext()) {
				sell = sellOrders.next();
				b:
				while (buyOrders.hasNext()) {
					buy = buyOrders.next();
					c:
					while (true){
						OrderPairs pairs = deal(rule, store, sell, buy);
						if(pairs == null){
							continue f;
						}
						if(sell.getAmount() <= 0){
							// 卖光
							needRemove.add(sell);
							if(buy.getAmount() <= 0){
								// 买光
								needRemove.add(buy);
								continue a;
							}else{
								// 未买光，下个卖单
								if(sellOrders.hasNext()){
									sell = sellOrders.next();
									continue c;
								}else{
									// 没有卖单了
									continue f;
								}
							}
						}else{
							// 未卖光
							if(buy.getAmount() <= 0){
								// 买光
								needRemove.add(buy);
								continue b;
							}else{
								// 未卖光未卖光，不存在
								_log.error("{} is wrong", pairs);
								stop = true;
								continue f;
							}
						}
					}
				}
			}
		}
		flag = 0;
		_log.info("matcher[{}] is stopped successfully", goods);
	}

	/**
	 *
	 * @param rule
	 * @param store
	 * @param sell
	 * @param buy
	 * @return
	 */
	private OrderPairs deal(MatchRule rule, OrderStore store, Order sell, Order buy){
		OrderPairs pairs = rule.match(this.goods, lastPrice, sell, buy);
		if(pairs == null){
			return null;
		}
		pairs.setGoodsSymbol(sell.getGoodsSymbol());
		pairs.setSeller(sell);
		pairs.setBuyer(buy);
		pairs.setTime(new Date());
		pairs.setLastPrice(lastPrice);

		// 更新数据
		lastPrice = pairs.getPrice();
		double lPrice = Double.parseDouble(lastPrice);

		if(ticker_open == null){
			ticker_open = lastPrice;
		}
		if(ticker_low == null){
			ticker_low = lastPrice;
		}else{
			ticker_low = this.goods.parse(Math.min(Double.parseDouble(ticker_low), lPrice));
		}
		if(ticker_high == null){
			ticker_high = lastPrice;
		}else{
			ticker_high = this.goods.parse(Math.max(Double.parseDouble(ticker_high), lPrice));
		}

		store.update(pairs);

		// 分配ID
		pairs.setId(IDUtils.generate("P"));

		if (debug)	_log.debug("pairsDone -> {}", pairs);

		// 通知观察者
		TickerData tickerData = new TickerData();
		tickerData.setSymbol(this.goods.getSymbol());
		tickerData.setPrice(pairs.getPrice());
		tickerData.setAmount(pairs.getAmount());
		tickerData.setOpen(ticker_open);
		tickerData.setClose(ticker_close);
		tickerData.setLow(ticker_low);
		tickerData.setHigh(ticker_high);

		notify(tickerData);

		return pairs;
	}

	/**
	 * 启动
	 */
	public synchronized void start(){
		if(flag == 0){
			flag = 1;
			this.worker = new Thread(() -> loop());
			this.worker.start();
		}else{
			_log.info("matcher[{}] is running or stopping", goods);
		}
	}

	/**
	 * 关闭
	 */
	public synchronized void shutdown(){
		if(flag == 1){
			stop = true;
			flag = 2;
			_log.info("tell matcher[{}] to stop", goods);
			try {
				this.worker.join();
			} catch (InterruptedException e) {
				_log.error(e, "matcher[{}] error", goods);
			}
		}
	}

	/**
	 * 开启之前调用
	 * 中断情况下，加载未完成的交易
	 * @param order
	 */
	public void loadUnfinishedOrder(List<Order> order){
		//
		if(flag == 0 && order != null){
			order.forEach(it -> this.orderStore.put(it));
		}
	}

	/**
	 * 加载data
	 * @param changeData
	 */
	public void loadData(ChangeData changeData){
		if(flag == 0 && changeData != null){
			if(TickerData.class.equals(changeData.getClass())){
				TickerData tickerData = (TickerData) changeData;
				lastPrice = formatPrice(tickerData.getPrice());
				ticker_open = formatPrice(tickerData.getOpen() == null ? lastPrice : tickerData.getOpen());
				ticker_close = formatPrice(tickerData.getClose());
				ticker_high = formatPrice(tickerData.getHigh() == null ? lastPrice : tickerData.getHigh());
				ticker_low = formatPrice(tickerData.getLow() == null ? lastPrice : tickerData.getLow());
				tickerData.setPrice(lastPrice);
				tickerData.setOpen(ticker_open);
				tickerData.setClose(ticker_close);
				tickerData.setLow(ticker_low);
				tickerData.setHigh(ticker_high);
			}
			init(changeData);
		}
	}

	/**
	 * 格式化价格
	 * @param price
	 * @return
	 */
	public String formatPrice(String price){
		if (price == null) {
			return null;
		}
		return this.goods.parse(Double.parseDouble(price));
	}

	@Override
	protected void call(int direction, String owner, String goodsSymbol, String price, String amount, String token) {
		if (!this.goods.getSymbol().equals(goodsSymbol)) {
			return;
		}
		OrderStore store = this.orderStore;

		Order order = new Order(direction
				, owner
				, goodsSymbol
				, goods.parse(Double.parseDouble(price))
				, Double.parseDouble(amount));
		if(store.put(order)){
			// 分配ID
			order.setId(IDUtils.generate("T"+order.getDirection()));
			order.setTime(System.currentTimeMillis());

			if (debug)	_log.debug("begin -> {}", order);
		}
	}

	@Override
	public String lastPrice() {
		return lastPrice;
	}

	@Override
	public boolean isOpen() {
		// 交易是否在开盘时间
		TradingPeriods tradingPeriods = this.goods.getTradingPeriods();
		if (tradingPeriods != null) {
			return tradingPeriods.isOpen();
		}
		return true;
	}

}
